package org.stragma.symbian.aterms;

import aterm.ATerm;
import aterm.ATermAppl;
import aterm.ATermList;

public class ATermStat implements IATermStat {

	private TermMapper mapper;
	
	/**
	 * Constructor
	 */
	public ATermStat() {
		this.mapper = new TermMapper();
	}
	
	/* (non-Javadoc)
	 * @see org.stragma.symbian.aterms.IATermStat#getTotalNodes(aterm.ATerm)
	 */
	@Override
	public long getTotalNodes(ATerm term) {
		long unique = 0;
		
		switch (term.getType()) {
			case ATerm.INT:
			case ATerm.REAL:
			case ATerm.BLOB:
			case ATerm.PLACEHOLDER:
				unique = 1;
				break;
				
			case ATerm.APPL:
				unique = 1;
				ATermAppl appl = (ATermAppl)term;
				int arity = appl.getArity();
				
				for(int i = 0; i < arity; i++) {
					unique += this.getTotalNodes(appl.getArgument(i));
				}
				
				break;
				
			case ATerm.LIST:
				
				ATermList list = (ATermList)term;
				while(!list.isEmpty()) {
					unique += this.getTotalNodes(list.getFirst());					
					list = list.getNext();
				}
								
				break;
				
		}
		
		return unique;
	}
	
	/* (non-Javadoc)
	 * @see org.stragma.symbian.aterms.IATermStat#getSubTerms(aterm.ATerm)
	 */
	@Override
	public long getSubTerms(ATerm term) {
		long unique = 0;
		
		// If a term has been annotated with other terms Incorporate this into the term count calculation
		ATermList annotations = term.getAnnotations();
		if ((annotations != null) && (!annotations.isEmpty())){
			unique += this.getSubTerms(annotations);
		}
		
		switch (term.getType()) {
			case ATerm.INT:
			case ATerm.REAL:
			case ATerm.BLOB:
			case ATerm.PLACEHOLDER:
				unique = 1;
				break;
				
			case ATerm.APPL:
				unique = 1;
				ATermAppl appl = (ATermAppl)term;
				int arity = appl.getArity();
				
				for(int i = 0; i < arity; i++) {
					unique += this.getSubTerms(appl.getArgument(i));
				}
				
				break;
				
			case ATerm.LIST:
				
				ATermList list = (ATermList)term;
				while(!list.isEmpty()) {
					unique += this.getSubTerms(list.getFirst());					
					list = list.getNext();
				}
				
				if (list.isEmpty()) {
					unique++;
				}
				
				break;
				
		}
		
		return unique;
	}

	/* (non-Javadoc)
	 * @see org.stragma.symbian.aterms.IATermStat#getUniqueSubTerms(aterm.ATerm)
	 */
	@Override
	public long getUniqueSubTerms(ATerm term) {
		long unique = 0;
		
		// If the term has been mapped then do not bother mapping again
		if (this.mapper.isMapped(term)) {
			return 0;
		} else {
			this.mapper.setMap(term);
		}
	
		// If a term has been annotated with other terms
		// Incorporate this into the term count calculation
		ATermList annotations = term.getAnnotations();
		if ((annotations != null) && (!annotations.isEmpty())){
			unique += this.getUniqueSubTerms(annotations);
		}
		
		switch (term.getType()) {
			case ATerm.INT:
			case ATerm.REAL:
			case ATerm.BLOB:
			case ATerm.PLACEHOLDER:
				unique = 1;
				break;
				
			case ATerm.APPL:
				unique = 1;
				ATermAppl appl = (ATermAppl)term;
				
				int arity = appl.getArity();
				
				for(int i = 0; i < arity; i++) {
					unique += this.getUniqueSubTerms(appl.getArgument(i));
				}
				
				break;
				
			case ATerm.LIST:
				
				ATermList list = (ATermList)term;
				
				while(!list.isEmpty()) {
					unique += this.getUniqueSubTerms(list.getFirst());					
					list = list.getNext();
				}
				
				if (list.isEmpty()) {
					unique++;
				}
				
				break;
				
		}

		return unique;
	}
}
